// Simple OpenGL example for CS184 sp08 by Trevor Standley, modified from sample code for CS184 on Sp06
#include <vector>
#include <iostream>
#include <fstream>
#include <cmath>
#include <cstring>
#include <string.h>
#include <time.h>
#include "triple.h"
#include "link.h"
#include "linkManager.h"
#include "Character.h"

using namespace Eigen;
using namespace std;

#ifdef _WIN32
#	include <windows.h>
#else
#	include <sys/time.h>
#endif

#ifdef OSX
#include <GLUT/glut.h>
#include <OpenGL/glu.h>
#else
#include <GL/glut.h>
#include <GL/glu.h>
#endif

#include <time.h>
#include <math.h>

#ifdef _WIN32
static DWORD lastTime;
#else
static struct timeval lastTime;
#endif

using namespace std;



//****************************************************
// Some Classes
//****************************************************
class Viewport {
public:
	int w, h; // width and height
};


struct polygon {
	vector<triple*> myVertices;
	Link* myLink;
};

//****************************************************
// Global Variables
//****************************************************
Viewport	viewport;
float trans_x;
float trans_y;
float rot_x;
float rot_y;
float scalar;
const float rot_step = 5;
bool mouseDown;
float x_coord;
float y_coord;
float z_coord;
GLuint characterDL;
vector<polygon> myPolygons;
Character stick_figure;
clock_t init;
//****************************************************
// reshape viewport if the window is resized
//****************************************************
void myReshape(int w, int h) {
	viewport.w = w;
	viewport.h = h;

	glViewport(0,0,viewport.w,viewport.h);// sets the rectangle that will be the window
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();				// loading the identity matrix for the screen

	//----------- setting the projection -------------------------
	// glOrtho sets left, right, bottom, top, zNear, zFar of the chord system


	// glOrtho(-1, 1 + (w-400)/200.0 , -1 -(h-400)/200.0, 1, 1, -1); // resize type = add
	// glOrtho(-w/400.0, w/400.0, -h/400.0, h/400.0, 1, -1); // resize type = center

	glOrtho(-1, 1, -1, 1, -100, 100);	// resize type = stretch

	//------------------------------------------------------------
}

void drawCharacter(){
	//glNormal3f( 1, 2, 3);
	/*for (int i = 0; i < myPolygons->size(); i++) {
		polygon* myPoly = myPolygons->at(i);
		vector<triple*> myPolyInfo = myPoly->myVertices;
		glBegin(GL_LINE_LOOP);
		for (int j=0; j < (int) myPolyInfo.size(); j++) {
			triple* myTriple = myPolyInfo.at(j);
			if (!myTriple->myNormal.isZero(0.0001)) {
				glNormal3f(myTriple->myNormal(0), myTriple->myNormal(1), myTriple->myNormal(2));
			}
			glVertex3f(myTriple->myVertex(0), myTriple->myVertex(1), myTriple->myVertex(2));

		}
		glEnd();
	}*/
}

GLuint createDL(){
	GLuint character;
	character = glGenLists(1);
	glNewList(character, GL_COMPILE);
	drawCharacter();
	glEndList();
	return character;
}
void associateLinks(Character& c, triple* t){
	float min = (t->myVertex - c.body[0]->getMid()).norm();
	Link* min_Link = c.body[0];
	for(int i =1; i<c.body.size(); i++){
		Link* temp = c.body[i];
		float dist =  (t->myVertex - c.body[i]->getMid()).norm();
		if(dist< min){
			min = dist;
			min_Link = c.body[i];
		}
	}
	t->myColor(0) = min_Link->r;
	t->myColor(1) = min_Link->g;
	t->myColor(2) = min_Link->b;

    Vector3f diff = t->myVertex - min_Link->getEnd();
	t->length= diff.norm();
    float t_global_theta = acos(diff[2]/t->length);
    float t_global_phi = atan2(diff[1],diff[0]);
    t->theta = t_global_theta - min_Link->global_theta;
    t->phi = t_global_phi - min_Link->global_phi;
	min_Link->vertices.push_back(t);

}
//****************************************************
// sets the window up
//****************************************************
void initScene(){
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Clear to black, fully transparent
	glEnable(GL_DEPTH_TEST); //enable Z-buffer
	myReshape(viewport.w,viewport.h);
	x_coord = 0;
	y_coord = 0;
	z_coord = 0;
	trans_x = 0;
	trans_y = 0;
	rot_x = 0;
	rot_y = 0;
	scalar = 1;
	mouseDown = false;
	init = clock();
	characterDL = createDL();

	for(int i=0; i<myPolygons.size(); i++){
		vector<triple*> vertices = myPolygons[i].myVertices;
		for(int j=0; j<vertices.size(); j++){
			associateLinks(stick_figure, vertices[j]);		
		}	
	}
	/*for (int i = 0; i < (int) myPolygons->size(); i++) {
		for(int j=0; j < (int) myPolygons->at(i)->myVertices.size(); j++) {
			Vector3f myVertex = myPolygons->at(i)->myVertices.at(j)->myVertex;
			float minLength = 1e308;
			triple* mintriple = NULL;
			Link* minLink = NULL;
			for(int k = 0; k < (int) stick_figure.body.size(); k++) {
				float dist = (myVertex - stick_figure.body.at(k)->getMid()).norm();
				if (dist < minLength) {
					minLength = dist;
					mintriple = myPolygons->at(i)->myVertices.at(j);
					minLink = stick_figure.body.at(k);
				}
			}
			mintriple->myDirection = mintriple->myVertex - minLink->getEnd();
			minLink->vertices.push_back(mintriple);
		}
	}*/

}

//***************************************************
// function that does the actual drawing
//***************************************************
void myDisplay() {

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// clear the color buffer (sets everything to black)
	// clear depth buffer
	glMatrixMode(GL_MODELVIEW);					// indicate we are specifying camera transformations

	glLoadIdentity();							// make sure transformation is "zero'd"


	//-----------------------set up lighting conditions--------------------
	float pos[4] = {1.5f, 1.0f, 1.0f, 0.0f};

	glLightfv(GL_LIGHT0, GL_POSITION, pos);

	float Ia[4] = {1.0f, 1.0f, 1.0f, 1.0f };
	glLightfv( GL_LIGHT0, GL_AMBIENT, Ia );	

	float Id[4] = {1.0f, 1.0f, 1.0f, 1.0f };
	glLightfv( GL_LIGHT0, GL_DIFFUSE, Id );	

	float Is[4] = {1.0f, 1.0f, 1.0f, 1.0f };
	glLightfv( GL_LIGHT0, GL_SPECULAR, Is );


	glEnable(GL_LIGHT0);

//	glEnable(GL_LIGHTING); //why enable twice??

	float ka[4] = {0.1f, 0.1f, 0.1f, 1.0f };
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ka );

	float kd[4] = {0.1f, 0.1f, 1.0f, 1.0f };
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, kd );

	float ks[4] = {0.9f, 0.9f, 0.9f, 1.0f };
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, ks );

	float p = 50.0f;
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, p );

	glEnable(GL_NORMALIZE);

	//----------------------- code to draw objects --------------------------
	// Rectangle Code
	
	glTranslatef(trans_x, trans_y, 0.0f);
	glRotatef(rot_x, 1.0f, 0.0f, 0.0f);
	glRotatef(rot_y, 0.0f, 1.0f, 0.0f);
	glScalef(scalar, scalar, scalar);
	
	//Vector3f goalPos(x_coord/scalar, y_coord/scalar, 0);
	
	double diff = clock() - init;
	if((diff/(double)CLOCKS_PER_SEC)>0.2){
		init = clock();
		stick_figure.walk(scalar);
	}
	//stick_figure.test(goalPos);
	//cout<<"x: "<<x_coord<<" y: "<<y_coord<<" : "<<scalar<<endl;
	stick_figure.display();
	/*
	LinkManager root;

	Vector3f goalPos(x_coord, y_coord, 0);

	
	Vector3f leftArm_origin(0 , 0, 0);
	root.initLink(leftArm_origin, 0, 0, 0.3);//3.14/2, 5*3.14/4, 0.3);
	root.addLink(0, 0, 0.3);
	root.addLink(0, 0, 0.3);


	root.calculate(goalPos);
	root.display();
	*/
	for (int i = 0; i < myPolygons.size(); i++) {
		polygon myPoly = myPolygons[i];
		vector<triple*> myPolyInfo = myPoly.myVertices;
		glBegin(GL_LINE_LOOP);
		for (int j=0; j < (int) myPolyInfo.size(); j++) {
			triple* myTriple = myPolyInfo.at(j);

			glColor3f(myTriple->myColor(0), myTriple->myColor(1), myTriple->myColor(2));
			if (!myTriple->myNormal.isZero(0.0001)) {
				glNormal3f(myTriple->myNormal(0), myTriple->myNormal(1), myTriple->myNormal(2));
			}

			glVertex3f(myTriple->myVertex(0), myTriple->myVertex(1), myTriple->myVertex(2));

		}
		glEnd();
	}
	//glCallList(characterDL);

	glutSwapBuffers();					// swap buffers (we earlier set double buffer)
}

//****************************************************
// called by glut when there are no messages to handle
//****************************************************
void myFrameMove() {
	//nothing here for now
#ifdef _WIN32
	Sleep(10);						//give ~10ms back to OS (so as not to waste the CPU)
#endif
	glutPostRedisplay(); // forces glut to call the display function (myDisplay())
}
//****************************************************
// called by glut when certain keys are pressed

//****************************************************
void keyboard(unsigned char c, int x, int y){
	if(c == ' ')
		exit(0);
	else if(c =='=' || c == '+')
		scalar *= 1.1;
	else if(c =='-' || c == '_')
		scalar /= 1.1;
	else{}
}

//****************************************************
// called by glut when special keys are pressed

//****************************************************
void specialKeyboard(int key, int x, int y) {
	int mod = 0;
	switch(key) {
	case GLUT_KEY_UP :
		mod = glutGetModifiers();
		if(mod == GLUT_ACTIVE_SHIFT)
			trans_y += 0.1;
		else
			rot_x -= rot_step;
		break;
	case GLUT_KEY_DOWN :
		mod = glutGetModifiers();
		if(mod == GLUT_ACTIVE_SHIFT)
			trans_y -= 0.1;
		else
			rot_x += rot_step;
		break;
	case GLUT_KEY_LEFT :
		mod = glutGetModifiers();
		if(mod == GLUT_ACTIVE_SHIFT)
			trans_x -= 0.1;
		else
			rot_y -= rot_step;
		break;
	case GLUT_KEY_RIGHT :
		mod = glutGetModifiers();
		if(mod == GLUT_ACTIVE_SHIFT)
			trans_x += 0.1;
		else
			rot_y += rot_step;
		break;
	}
}

void mouse(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){
		mouseDown = true;
		x_coord = 2.0*x/viewport.w - 1 ;
		y_coord = - 2.0*y/viewport.h + 1;
	}
	else
		mouseDown = false;
}

void mouseMotion(int x, int y)
{
	if (mouseDown)
	{
		x_coord = 2.0*x/viewport.w - 1 ;
		y_coord = - 2.0*y/viewport.h + 1;
		glutPostRedisplay();
	}
}


void parseText(char* name) {
	vector<Vector3f> myVertices;
	vector<Vector3f> myColors; 
	vector<Vector3f> myNormals; 

	char str[255];
	float x;
	float y;
	float z;

	fstream myFile;
	myFile.open(name);

	if(myFile.fail()){
		cout<<"failed open input file!"<<endl;
		exit(1);
	}

	while (!myFile.eof()) {
		myFile >> str;
		if (strcmp(str, "v") == 0) {
			myFile >> x;
			myFile >> y;
			myFile >> z;
			Vector3f* tempvec= new Vector3f(x, y, z);
			myVertices.push_back(*tempvec);
		}

		if (strcmp(str, "vt") == 0) {
			myFile >> x;
			myFile >> y;
			myFile >> z;
			Vector3f* tempvec= new Vector3f(x, y, z);
			myColors.push_back(*tempvec);
		}

		if (strcmp(str, "vn") == 0) {
			myFile >> x;
			myFile >> y;
			myFile >> z;
			Vector3f* tempvec= new Vector3f(x, y, z);
			myNormals.push_back(*tempvec);
		}

		if (strcmp(str, "f") == 0) {
			int a;
			int b;
			int c;
			myFile>>a;
			myFile>>b;
			myFile>>c;

			triple* triple1 = new triple();
			triple* triple2 = new triple();
			triple* triple3 = new triple();

			triple1->myVertex = myVertices[a-1];
			triple2->myVertex = myVertices[b-1];
			triple3->myVertex = myVertices[c-1];
			
			polygon* temp = new polygon();
			temp->myVertices.push_back(triple1);
			temp->myVertices.push_back(triple2);
			temp->myVertices.push_back(triple3);

			myPolygons.push_back(*temp);
		}
	}

}

void parseArgs(int argc, char* argv[]) {
	//parseText("chair.obj");
	cout<<"starting parsing"<<endl;
	parseText(argv[1]);
	cout<<"finishing parsing"<<endl;
}

//****************************************************
// the usual stuff, nothing exciting here
//****************************************************
int main(int argc, char *argv[]) {


	
	parseArgs(argc, argv);
	//This initializes glut
	glutInit(&argc, argv);

	//This tells glut to use a double-buffered window with red, green, and blue channels
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); //enable Z-buffer

	// Initalize theviewport size
	viewport.w = 800;
	viewport.h = 800;

	//The size and position of the window
	glutInitWindowSize(viewport.w, viewport.h);
	glutInitWindowPosition(0, 0);
	glutCreateWindow("Assignemtn5");

	initScene();							// quick function to set up scene
	

	glutKeyboardFunc(keyboard);             // function to run when certain key strokes are detected
	glutSpecialFunc(specialKeyboard);      // function to call when special key strokes are detected
	glutMouseFunc(mouse);
	glutMotionFunc(mouseMotion);
	glutDisplayFunc(myDisplay);				// function to run when its time to draw something
	glutReshapeFunc(myReshape);				// function to run when the window gets resized
	glutIdleFunc(myFrameMove);				// function to run when not handling any other task
	glutMainLoop();							// infinite loop that will keep drawing and resizing and whatever else
	
	return 0;
}

//cout << "iteration" << endl;
			char temp[255];
			polygon myPoly;
			myFile.getline(temp, 256);

			char* temp1 = strtok(temp, " ");
			vector<char*> list = vector<char*>();
			list.push_back(temp1);

			while (temp1) {
				//cout << temp1 << endl;
				temp1 = strtok(NULL, " ");
				list.push_back(temp1);
			}

			//cout << (int) list.size() << endl;

			for (int i = 0; i < (int) list.size() - 1; i++) {
				char* temp2 = list.at(i);
				//cout << *temp2 << endl;
				char* p = strtok(temp2, "/");

				int a = 0;
				int b = 0;
				int c = 0;
				int ii = 1;

				while (p) {
					//cout << *p << endl;
					if (ii == 1) {
						a = atoi(p);
						//cout << a << endl;
					}
					if (ii == 2) {
						b = atoi(p);
						//cout << b << endl;
					}
					if (ii == 3) {
						c = atoi(p);
						//cout << c << endl;
					}
					p = strtok(NULL, "/");
					ii++;
				}
				triple* myTriple = new triple;
				if (a) {
					myTriple->myVertex = myVertices.at(a - 1);
				} else {
					myTriple->myVertex = Vector3f(0, 0, 0);
				}
				if (b) {
					myTriple->myColor = myColors.at(b - 1);
				} else {
					myTriple->myColor = Vector3f(0, 0, 0);
				}
				if (c) {
					myTriple->myNormal = myNormals.at(c - 1);
				} else {
					myTriple->myNormal = Vector3f(0, 0, 0);
				}

				myPoly.myVertices.push_back(myTriple);
			}
			myPolygons.push_back(myPoly);